#pragma once

#include "zoomviewpanel.h"
#include "sprite.h"
#include "sanimationviewer.h"
#include "sclippallette.h"

class FrameZoomViewPanel : public ZoomViewPanel
{
protected:
	/** sprite being edited by this viewer */
	Sprite * m_sprite;
	/** which clip in the sprite's current frame is selected */
	int m_selectedClipIndex;
	/** true when the user is dragging the selected clip */
	bool m_draggingClip;
	/** which clip pallette the frame editor gets it's clips from */
	SClipPallette * m_clipPallette;
	/** which frame editor must be notified when the frame changes size/shape */
	SAnimationViewer * m_animationViewer;
	/** there is currently no selected clip */
	static const int NO_SELECTION = -1;
	/** distance selected clip was from mouse when clicked on */
	SPoint m_clickedOffset;
public:
	/** where on the **screen** the origin should be */
	SPoint origin()
	{
		SPoint o(getArea()->getPosition());
		if(m_sprite->getAnimationCount() && m_sprite->getFrameCount())
		{
			int index = m_sprite->getFrameIndex();
			m_sprite->setFrameIndexSafe(index);
			SFrame * f = m_sprite->getFrame();
			if(f)
			{
				SRect frame(*f->getFrameSize());
				o.subtract(frame.getPosition().product(m_zoom));
			}
		}
		return o;
	}
	void setArea(const SRect & a_rect)
	{
		ZoomViewPanel::setArea(a_rect);
	}
	void setZoom(int a_zoom, SPoint a_zoomCenter)
	{
		// the origin lines extend into the parent
		m_parent->setDirty(0);
		ZoomViewPanel::setZoom(a_zoom, a_zoomCenter);
	}
	SRect * frameSize()
	{
		SFrame * f = m_sprite->getFrame();
		if(f)
		{
			return f->getFrameSize();
		}
		return 0;
	}
	void refreshArea()
	{
//		setDirty(0);
		m_parent->setDirty(0);
		SRect * f = frameSize();
		if(f)
		{
			SRect frame(*f);
			getArea()->setDimension(frame.getDimension().product(m_zoom));
			setDirty(0);
		}
	}
	void setClipPallette(SClipPallette * a_clipPallette){
		m_clipPallette = a_clipPallette;
	}
	void setAnimationViewer(SAnimationViewer * a_animationViewer){
		m_animationViewer = a_animationViewer;
	}
	FrameZoomViewPanel():m_sprite(0),//m_selected(0),
		m_draggingClip(false),m_clipPallette(0),m_selectedClipIndex(NO_SELECTION){}
	inline void setSprite(Sprite * a_sprite)
	{
		m_sprite = a_sprite;
	}
	void draw(SDL_Surface * a_surface)
	{
		if(m_sprite->getFrameCount() <= 0)
		{
			VSDL_drawString("No Frame! You must create a Frame! [+F]", 
				a_surface, this->getArea()->getPosition());
			return;
		}
		SPoint p = origin();
		SRect drawableArea(*m_parent->getArea());
		// draw center
		SRect r(drawableArea.getX(), p.y, drawableArea.getWidth(), 1);
		VSDL_FillRect(a_surface, &r, -1);
		r.set(p.x, drawableArea.getY(), 1, drawableArea.getHeight());
		VSDL_FillRect(a_surface, &r, -1);
		if(m_sprite && m_sprite->isSprite())
		{
			m_sprite->draw(a_surface, p, m_zoom);
		}
		if(m_selectedClipIndex != NO_SELECTION)
		{
			// prevent crash when sprite changes during draw TODO move this logic to someplace else...
			if(m_selectedClipIndex >= m_sprite->getFrame()->getClipCount())
			{
				m_selectedClipIndex = NO_SELECTION;
			}
			else
			{
				SRect area = m_sprite->getFrame()->getClip(m_selectedClipIndex)->getRect();
				SRect converted = this->getViewAreaOfPixelRectangle(area);
				VSDL_drawRectBroken(a_surface, (SDL_Rect*)&converted, -1, 2, 2);
			}
		}
	}
	/** keeps the origin in the same spot when the frame changes */
	void changeFrame(
		const int & a_oldAnimation, const int & a_oldFrame, 
		const int & a_newAnimation, const int & a_newFrame)
	{
		setDirty(0);
		m_selectedClipIndex = NO_SELECTION;

		if(a_oldFrame != m_sprite->getFrameIndex()
		|| a_oldAnimation != m_sprite->getAnimationIndex())
		{
			m_sprite->setAnimationIndex(a_oldAnimation);
			m_sprite->setFrameIndex(a_oldFrame);
			refreshArea();
		}
		SPoint oldOrigin(origin());
		SFrame * nextFrame = m_sprite->getFrame();
//		if(nextFrame)
//		{
//			SRect frame(*nextFrame->getFrameSize());
			SPoint oldUpperLeft(getArea()->getPosition());

			m_sprite->setAnimationIndex(a_newAnimation);
			m_sprite->setFrameIndex(a_newFrame);
			refreshArea();

			SPoint newOrigin(origin());
			getArea()->setPosition(oldUpperLeft.sum(oldOrigin.difference(newOrigin)));
//		}
		setDirty(0);
	}
	void advanceFrame(const int & a_frames)
	{
		int nextFrameIndex = m_sprite->getFrameIndex()+a_frames;
		if(nextFrameIndex < 0)
			nextFrameIndex = m_sprite->getAnimation()->getFrameCount()-1;
		else if(m_sprite->getAnimation()->getFrameCount() != 0)
			nextFrameIndex %= m_sprite->getAnimation()->getFrameCount();
		int anim = m_sprite->getAnimationIndex();
		changeFrame(anim, m_sprite->getFrameIndex(), anim, nextFrameIndex);
	}
	void addSelectedClip()
	{
		ImageClip * addedClip = m_clipPallette->getSelectedClip();
		SFrame * f = m_sprite->getFrame();
		if(f && addedClip)
		{
			f->getClips()->add(SClip(addedClip));
			m_sprite->getFrame()->invalidateFrameSize();
			m_animationViewer->refresh();
			setDirty(0);
		}
	}
	void delSelectedClip()
	{
		if(m_selectedClipIndex != NO_SELECTION)
		{
			TemplateArray<SClip> * clips = m_sprite->getFrame()->getClips();
			clips->remove(m_selectedClipIndex);
			m_selectedClipIndex = NO_SELECTION;
			m_sprite->getFrame()->invalidateFrameSize();
			m_animationViewer->refresh();
			setDirty(0);
		}
	}
	void movSelectedClip(const int & a_delta)
	{
		if(m_selectedClipIndex != NO_SELECTION)
		{
			TemplateArray<SClip> * clips = m_sprite->getFrame()->getClips();
			int nextIndex = m_selectedClipIndex + a_delta;
			int size = clips->size();
			if(size > 0)
			{
				while(nextIndex >= size)
					nextIndex -= size;
				while(nextIndex < 0)
					nextIndex += size;
				clips->swap(m_selectedClipIndex, nextIndex);
				m_selectedClipIndex = nextIndex;
			}
			m_sprite->getFrame()->invalidateFrameSize();
			m_animationViewer->refresh();
			setDirty(0);
		}
	}
	void flipSelectedClip(const int & a_flip)
	{
		if(m_selectedClipIndex != NO_SELECTION)
		{
			SClip * clip = m_sprite->getFrame()->getClip(m_selectedClipIndex);
			clip->applyFlip(a_flip);
			// TODO only refresh the area in the animation viewer that changed...
			m_animationViewer->refresh();
		}
	}
	void deleteFrame()
	{
		int index = m_sprite->getFrameIndex();
		if(index >= 0 && m_sprite->getFrameCount() > 0)
		{
			m_sprite->getAnimation()->getFrames()->remove(index);
			refreshArea();
			m_animationViewer->refresh();
			setDirty(0);
		}
	}
	void deleteAnimation()
	{
		int index = m_sprite->getAnimationIndex();
		int animCount = m_sprite->getAnimationCount();
		if(index >= 0 && animCount > 0)
		{
			m_sprite->getAnimations()->remove(index);
			refreshArea();
			m_animationViewer->hardRefresh();
			m_animationViewer->refresh();
			setDirty(0);
		}
	}
	bool handleEvent(const SDL_Event &e)
	{
		bool handled = false;
		bool dirty = false;
		SPoint o = origin();
		switch(e.type)
		{
		case SDL_MOUSEBUTTONDOWN:
			if(e.button.button == SDL_BUTTON_LEFT)
			{
				SPoint click(e.button.x, e.button.y);
				//click.subtract(o);
				//SPoint pixel = click.quotient(m_zoom);
				SPoint pixel = pixelPointedAt(click);
				SFrame * frame = m_sprite->getFrame();
				SClip * clip;
				SRect rect;
				if(frame)
				{
					for(int i = frame->getClipCount()-1; !handled && i >= 0; --i)
					{
						clip = frame->getClip(i);
						rect = clip->getRect();
						if(rect.contains(pixel))
						{
							//printf("clip %d selected [%d %d %d %d]\n", 
							//	i, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
							handled = true;
							//m_selected = clip;
							dirty |= m_selectedClipIndex != i;
							m_selectedClipIndex = i;
							m_clickedOffset = pixel.difference(rect.getPosition());
							printf("%d %d\n", m_clickedOffset.getX(), m_clickedOffset.getY());
						}
					}
				}
				if(!handled)
				{
					//printf("no clip selected\n");
					//m_selected = 0;
					dirty |= m_selectedClipIndex != NO_SELECTION;
					m_selectedClipIndex = NO_SELECTION;
				}
				m_draggingClip = m_selectedClipIndex != NO_SELECTION;//m_selected != 0;
			}
			break;
		case SDL_MOUSEBUTTONUP:
			if(m_draggingClip)
			{
				m_draggingClip = false;
				if(m_animationViewer)
					m_animationViewer->refresh();
				handled = true;
			}
			break;
		case SDL_MOUSEMOTION:
			if(m_draggingClip)
			{
				SPoint pixel = pixelPointedAt(SPoint(e.button.x, e.button.y));
				m_sprite->getFrame()->getClip(m_selectedClipIndex)->getOffset()->
					set(pixel.difference(m_clickedOffset));
				dirty = true;
			}
			break;
		case SDL_KEYDOWN:
			if(m_selectedClipIndex != NO_SELECTION)
			{
				SPoint move;
				SRect frame(*frameSize());
				SPoint * offset = m_sprite->getFrame()->getClip(m_selectedClipIndex)->getOffset();
				switch(e.key.keysym.sym)
				{
				case SDLK_UP:		move.set(0,-1);	dirty = true;	break;
				case SDLK_DOWN:		move.set(0, 1);	dirty = true;	break;
				case SDLK_RIGHT:	move.set( 1,0);	dirty = true;	break;
				case SDLK_LEFT:		move.set(-1,0);	dirty = true;	break;
				}
				if(!move.isZero())
				{
					// move the clip
					offset->add(move);
					m_sprite->getFrame()->invalidateFrameSize();
					//SRect newFrame(*frameSize());
					//// if the frame size has altered
					//if(!frame.equals(newFrame))
					//{
					//	// will keep the origin in the same place as the frame moves...
					//	SPoint oldPos(frame.getPosition());
					//	getArea()->setPosition(oldPos.sum(move.product(m_zoom)));
					//}
				}
				if(m_animationViewer)
					m_animationViewer->refresh();
			}
			if(!handled)
			{
				switch(e.key.keysym.sym)
				{
				case SDLK_SPACE:		advanceFrame(+1);	dirty = true;	break;
				case SDLK_BACKSPACE:	advanceFrame(-1);	dirty = true;	break;
				case SDLK_INSERT:		addSelectedClip();	dirty = true;	break;
				case SDLK_DELETE:		
					if(e.key.keysym.mod & KMOD_SHIFT){	deleteAnimation();	}
					else if(e.key.keysym.mod & KMOD_CTRL){	deleteFrame();	}
					else{								delSelectedClip();	}
					dirty = true;	break;
				case SDLK_PAGEUP:		movSelectedClip(+1);	dirty = true;	break;
				case SDLK_PAGEDOWN:		movSelectedClip(-1);	dirty = true;	break;
				}
			}
			break;
		}
		if(!(handled || dirty))
			handled = ZoomViewPanel::handleEvent(e);
		if(dirty)refreshArea();
		return handled || dirty;
	}
};